package io.github.tuuzed.hydrogen;

import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

import io.github.tuuzed.hydrogen.util.Logger;


public class From {
    private static final String TAG = "Hydrogen.From";
    private Class<? extends Model> mClazz;
    private String mTableName;
    private String[] mColumns;
    private boolean isDistinct;
    private String mOrderBy;
    private String mGroupBy;
    private String mHaving;
    private String mLimit;
    private String mSelection;
    private String[] mSelectionArgs;


    From(Class<? extends Model> clazz, boolean isDistinct, String[] columns) {
        mClazz = clazz;
        mTableName = Hydrogen.getInstance().getTableName(clazz);
        this.isDistinct = isDistinct;
        mColumns = columns;
    }

    public From where(String selection, String[] selectionArgs) {
        mSelection = selection;
        mSelectionArgs = selectionArgs;
        return this;
    }

    public From groupBy(String groupBy) {
        mGroupBy = groupBy;
        return this;
    }

    public From having(String having) {
        mHaving = having;
        return this;
    }

    public From orderBy(String orderBy) {
        mOrderBy = orderBy;
        return this;
    }

    public From limit(String limit) {
        mLimit = limit;
        return this;
    }

    public <T extends Model> T first() {
        SQLiteDatabase db = Hydrogen.getInstance().getReadableDatabase();
        Cursor cursor = query(db);
        if (cursor.moveToFirst()) {
            T model = processCursor(cursor, mColumns);
            cursor.close();
            db.close();
            return model;
        }
        cursor.close();
        db.close();
        return null;
    }

    public <T extends Model> T last() {
        SQLiteDatabase db = Hydrogen.getInstance().getReadableDatabase();
        Cursor cursor = query(db);
        if (cursor.moveToLast()) {
            T model = processCursor(cursor, mColumns);
            cursor.close();
            db.close();
            return model;
        }
        cursor.close();
        db.close();
        return null;
    }

    public <T extends Model> List<T> all() {
        SQLiteDatabase db = Hydrogen.getInstance().getReadableDatabase();
        Cursor cursor = query(db);
        List<T> list = new ArrayList<>();
        if (cursor.moveToFirst()) {
            do {
                list.add(this.<T>processCursor(cursor, mColumns));
            } while (cursor.moveToNext());
        }
        cursor.close();
        db.close();
        return list;
    }

    private Cursor query(SQLiteDatabase db) {
        return db.query(isDistinct, mTableName, mColumns, mSelection,
                mSelectionArgs, mGroupBy, mHaving, mOrderBy, mLimit);
    }

    private <T extends Model> T processCursor(Cursor cursor, String[] columns) {
        List<Hydrogen.ColumnInfo> columnInfoList = Hydrogen.getInstance().getColumnInfoList(mClazz);
        try {
            T model = (T) mClazz.newInstance();
            for (Hydrogen.ColumnInfo columnInfo : columnInfoList) {
                Field field = columnInfo.getField();
                String columnName = columnInfo.getColumnName();
                field.setAccessible(true);
                for (String col : columns) {
                    if (col.equals(columnName)) {
                        fieldValue(field, model, cursor, columnName);
                    }
                }
            }
            return model;
        } catch (Exception e) {
            e.printStackTrace();
            Logger.e(TAG, e.toString(), e);
        }
        return null;
    }

    private void fieldValue(Field field, Model model, Cursor cursor, String columnName)
            throws IllegalAccessException, InstantiationException {
        model.setId(cursor.getInt(cursor.getColumnIndex("_id")));
        final String type = field.getType().getName();
        if (String.class.getName().equals(type)) {
            // String
            field.set(model, cursor.getString(cursor.getColumnIndex(columnName)));
        } else if ("char".equals(type) || Character.class.getName().equals(type)) {
            // Char or char
            field.set(model, (char) cursor.getString(cursor.getColumnIndex(columnName))
                    .getBytes()[0]);
        } else if (Integer.class.getName().equals(type) || "int".equals(type)) {
            // Integer or int
            field.set(model, cursor.getInt(cursor.getColumnIndex(columnName)));
        } else if (Boolean.class.getName().equals(type) || "boolean".equals(type)) {
            // Boolean or boolean
            field.set(model, cursor.getInt(cursor.getColumnIndex(columnName)) == 1);
        } else if (Byte.class.getName().equals(type) || "byte".equals(type)) {
            // Byte or byte
            field.set(model, (byte) cursor.getInt(cursor.getColumnIndex(columnName)));
        } else if (Double.class.getName().equals(type) || "double".equals(type)) {
            // Double or double
            field.set(model, cursor.getDouble(cursor.getColumnIndex(columnName)));
        } else if (type.equals(Float.class.getName()) || type.equals("float")) {
            // Float or float
            field.set(model, cursor.getFloat(cursor.getColumnIndex(columnName)));
        } else if (Short.class.getName().equals(type) || "short".equals(type)) {
            // Short or short
            field.set(model, cursor.getShort(cursor.getColumnIndex(columnName)));
        } else if (Long.class.getName().equals(type) || "long".equals(type)) {
            // Long or long
            field.set(model, cursor.getLong(cursor.getColumnIndex(columnName)));
        }
    }

}
